﻿using System.Collections.Generic;
using MessageMachine;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework;

namespace InputXMachine
{
    public class Input360 : ISubscriber
    {
        private List<GamePadState> lastPadState;
        private List<GamePadState> padState;
        private Dictionary<Buttons, int> buttonsSubscribed;
        //Static only to make use of the AddKey(...) method.
        private static int msgAddKey, msgRemoveKey, msgClearKeys;
        private static int msgLeftStickX, msgLeftStickY;

        /// <summary> Create a new input manager which uses XNA.</summary>
        /// <param name="msgAddKey">ExtraInfo is of type System.Tuple(Xna.Input.Keys, int).</param>
        /// <param name="msgRemoveKey">ExtraInfo is of type Xna.Input.Keys.</param>
        /// <param name="msgClearKeys">ExtraInfo is of type Xna.Input.Keys.</param>
        public Input360(int msgAddKey, int msgRemoveKey, int msgClearKeys,
            int msgLeftStickX, int msgLeftStickY)
        {
            buttonsSubscribed = new Dictionary<Buttons, int>();
            Input360.msgAddKey = msgAddKey;
            Input360.msgRemoveKey = msgRemoveKey;
            Input360.msgClearKeys = msgClearKeys;
            Input360.msgLeftStickX = msgLeftStickX;
            Input360.msgLeftStickY = msgLeftStickY;
            MessagePasser.Subscribe(new int[] { msgAddKey, msgRemoveKey, msgClearKeys, msgLeftStickX, msgLeftStickY }, this);
            lastPadState = new List<GamePadState>(4);
            padState = new List<GamePadState>(4);
            for (int i = 0; i < 4; i++)
            {
                lastPadState.Add(new GamePadState());
                padState.Add(new GamePadState());
            }
        }

        /// <summary>
        /// Updates the connected game pads and sends messages to anyone subscribing to
        /// recently pressed and released buttons.
        /// </summary>
        public bool Update(PlayerIndex playerIndex)
        {
            padState[(int)playerIndex] = GamePad.GetState(playerIndex);
            if (padState[(int)playerIndex].IsConnected)
            {
                foreach (Buttons b in buttonsSubscribed.Keys)
                {
                    if (padState[(int)playerIndex].IsButtonDown(b) && lastPadState[(int)playerIndex].IsButtonUp(b))
                        MessagePasser.Send(new Message(buttonsSubscribed[b], true, this));
                    else if (padState[(int)playerIndex].IsButtonUp(b) && lastPadState[(int)playerIndex].IsButtonDown(b))
                        MessagePasser.Send(new Message(buttonsSubscribed[b], false, this));
                }
                if (padState[(int)playerIndex].ThumbSticks.Left != lastPadState[(int)playerIndex].ThumbSticks.Left)
                {
                    MessagePasser.Send(new Message(msgLeftStickX, padState[(int)playerIndex].ThumbSticks.Left.X, this));
                    MessagePasser.Send(new Message(msgLeftStickY, padState[(int)playerIndex].ThumbSticks.Left.Y, this));
                }
                lastPadState[(int)playerIndex] = padState[(int)playerIndex];
                return true;
            }
            else
                return false;
        }

        /// <summary>
        /// Updates the connected game pads and sends messages to anyone subscribing to
        /// recently pressed and released buttons.
        /// </summary>
        public void Update()
        {
            for (int i = (int)PlayerIndex.One; i <= (int)PlayerIndex.Four; i++)
            {
                padState[i] = GamePad.GetState((PlayerIndex)i);
                GamePadType type = GamePad.GetCapabilities((PlayerIndex)i).GamePadType;
                if (padState[i].IsConnected && (type == GamePadType.ArcadeStick
                    || type == GamePadType.BigButtonPad
                    || type == GamePadType.DancePad
                    || type == GamePadType.GamePad))
                {
                    foreach (Buttons b in buttonsSubscribed.Keys)
                    {
                        if (padState[i].IsButtonDown(b) && lastPadState[i].IsButtonUp(b))
                            MessagePasser.Send(new Message(buttonsSubscribed[b], true, this));
                        else if (padState[i].IsButtonUp(b) && lastPadState[i].IsButtonDown(b))
                            MessagePasser.Send(new Message(buttonsSubscribed[b], false, this));
                    }
                    if (padState[i].ThumbSticks.Left != lastPadState[i].ThumbSticks.Left)
                    {
                        MessagePasser.Send(new Message(msgLeftStickX, padState[i].ThumbSticks.Left.X, this));
                        MessagePasser.Send(new Message(msgLeftStickY, padState[i].ThumbSticks.Left.Y, this));
                    }
                    lastPadState[i] = padState[i];
                }
            }
        }

        /// <summary>
        /// Handles both sending a message to add the key and arranging a
        /// subscription for the subscriber.
        /// </summary>
        /// <param name="key">The key to add.</param>
        /// <param name="keySubscription">The msg# to be sent when the key is used.</param>
        /// <param name="subscriber">Whom to subscribe to this key.</param>
        public static void AddPadButton(Buttons button, int keySubscription, ISubscriber subscriber)
        {
            MessagePasser.Send(new Message(msgAddKey,
                new BasicParts.Tuple<Buttons, int>(button, keySubscription), subscriber));
            MessagePasser.Subscribe(keySubscription, subscriber);
        }

        #region ISubscriber Members
        public void Receive(Message msg)
        {
            if (msg.msgType == msgAddKey)
            {
                BasicParts.Tuple<Buttons, int> t = (BasicParts.Tuple<Buttons, int>)msg.extraInfo;
                if (!buttonsSubscribed.ContainsKey(t.Item1)) buttonsSubscribed.Add(t.Item1, t.Item2);
            }
            else if (msg.msgType == msgRemoveKey) //will probly cause errors due to collection change
            {
                buttonsSubscribed.Remove((Buttons)msg.extraInfo);
            }
            else if (msg.msgType == msgClearKeys) //will probly cause errors due to collection change
            {
                buttonsSubscribed.Clear();
            }
        }
        #endregion
    }
}